OS: Windows 10
Editor: Visual Studio Code
Rust version: 1.63.0
當程式發生致命錯誤的時候,通常最直接的做法是會把錯誤訊息輸出出來,然後緊急關閉程式,一樣的,Rust也有這樣的功能:
panic!("??");
執行後會印出以下訊息:
thread 'main' panicked at '??', src\main.rs:5:5
error: process didn't exit successfully: `target\debug\basic.exe` (exit code: 101)
這樣的錯誤訊息,之前也在別的篇章見過,但沒注意到的是下面還有一段訊息,可以把一步步地把錯誤往回推:
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
在教學文件中,可以遍執行的時候,多下這行指令把錯誤往回推:
RUST_BACKTRACE=1 cargo run
但這貌似只在Unix跟macOS才有效,稍微改一下,把這指令設定在main()
裡面:
use std::env;
fn main() {
env::set_var("RUST_BACKTRACE", "1");
panic!("??");
}
Result
與可復原錯誤Result
之前也有見過,在找main()
是否可以回傳錯誤的時候有登場。
這個功能對比到其他語言的話就是try...catch...
。
他的型別是Result<T, E>
,類似Option<T>
,但比起空值,他給的是錯誤,以下是例子:
use std::fs::File;
// ...
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Failed to open file: {:?}", error),
}
{:?}
這個format是debug的format,是要讓結構去實作std::fmt::Debug
這個介面,才可以讓結構直接輸出成特定格式。
再來,如果給出錯誤,我們可以對於一些特定的錯去篩選跟處理,例如:
use std::fs::File;
use std::io::ErrorKind;
// ...
// 沒有檔案的話幫忙建一個
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
if error.kind() == ErrorKind::NotFound {
match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("{:?}", e),
}
} else {
panic!("{:?}", error);
}
}
};
上面的處理雖然詳細,但冗長,我們可以用unwrap()
跟expect()
來簡化它。
// 由系統幫忙輸出panic內容
let f = File::open("hello.txt").unwrap();
// 指定輸出apnic的內容
let f = File::open("hello.txt").expect("File to open \"hello.txt\".");
use std::fs::File;
use std::io::{self, Read};
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
let f = File::open(filename);
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e),
}
}
fn main() {
let greeting = read_file_to_string("hello.txt");
if let Ok(content) = greeting {
println!("{}", content);
} else if let Err(e) = greeting {
panic!("{:?}", e);
}
}
試試看以上個這個範例,如果有hello.txt
的話,會輸出文件的內容,沒有的話則會丟出panic
。
然後我們可以用**?
** 運算子簡化read_file_to_string
,這個運算子如果是Ok
的話,則會以表達式(expression)回傳;如果是Err
的話,則會連帶return
,把整個函式回傳回去:
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
let mut f = File::open(filename)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
如果要再簡化,可以這樣寫:
fn read_file_to_string(filename: &str) -> Result<String, io::Error> {
let mut s = String::new();
File::open(filename)?.read_to_string(&mut s)?;
Ok(s)
}